package com.senseauto.ah8mediacodec;

import android.content.Context;
import android.content.res.Configuration;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.text.TextUtils;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import com.senseauto.ah8mediacodec.utils.ByteBuffer1080Pool;
import com.senseauto.ah8mediacodec.utils.YUVUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * 某些车机可能用不了camera1的api方法，得用新版的camera2的api方法
 * （也有些平板和手机camera1和camera2的都可以用）
 */
public class CameraActivity extends AppCompatActivity   implements View.OnClickListener {
    String TAG = "CameraActivity";

    private int mPreviewWidth = 1920;
    private int mPreviewHeight = 1080;
    private int cameraId = 0;

    private AutoFitTextureView mTextureView;
    private CameraDevice mCameraDevice;

    private CameraCaptureSession mPreviewSession;
    private CameraCaptureSession mRecordSession;

    private HandlerThread mBackgroundThread;
    private Handler mBackgroundHandler;
    private ImageReader mImageReader;
    private Surface mRecordSurface;//录制的surface
    private Surface mPreviewSurface;//预览的surface
    private CaptureRequest.Builder mPreviewBuilder;
    private CaptureRequest.Builder mRecordPreviewBuilder;
    private CaptureRequest previewCaptureRequest;
    private Size mPreviewSize;
    private Size mVideoSize;
    private boolean isRecording = false;

    private void startBackgroundThread() {
        mBackgroundThread = new HandlerThread("CameraBackground");
        mBackgroundThread.start();
        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
    }

    /**
     * Stops the background thread and its {@link Handler}.
     */
    private void stopBackgroundThread() {
        if (mBackgroundThread != null) {
            mBackgroundThread.quitSafely();
            try {
                mBackgroundThread.join();
                mBackgroundThread = null;
                mBackgroundHandler = null;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {

        @Override
        public void onOpened(@NonNull CameraDevice cameraDevice) {
            Log.e(TAG, "onOpened");
            mCameraDevice = cameraDevice;

            if (null != mTextureView) {
                configureTransform(mTextureView.getWidth(), mTextureView.getHeight());
            }

            startPreview();
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice cameraDevice) {
            Log.e(TAG, "onDisconnected");
            cameraDevice.close();
            mCameraDevice = null;
        }

        @Override
        public void onError(@NonNull CameraDevice cameraDevice, int error) {
            Log.e(TAG, "onError error="+error);

            cameraDevice.close();
            mCameraDevice = null;
            finish();
        }

    };
    public void stopPreview(){
        if(mPreviewSession!=null){
            try {
                mPreviewSession.stopRepeating();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    private void startPreview() {
        if (null == mCameraDevice || !mTextureView.isAvailable() || null == mPreviewSize) {
            return;
        }
        try {
            //Log.e(TAG, "startPreview()-->下面有closePreviewSession()");
            //closePreviewSession();//这里注释了！！！不然会出现updatePreview(Camera2Activity.java:743)里报错IllegalStateException: Session has been closed; further changes are illegal.
            SurfaceTexture texture = mTextureView.getSurfaceTexture();
            assert texture != null;
            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
            mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

            mPreviewBuilder.addTarget(mPreviewSurface);

            List<Surface>list = new ArrayList<>();
            list.add(mPreviewSurface);

            mCameraDevice.createCaptureSession(list,
                    new CameraCaptureSession.StateCallback() {

                        @Override
                        public void onConfigured(@NonNull CameraCaptureSession session) {
                            mPreviewSession = session;
                            Log.e(TAG, "startPreview()-->mCameraDevice.createCaptureSession-->onConfigured");
                            try {
                                CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

                                if(mPreviewSurface!=null){
                                    // 用于承载预览显示流
                                    Log.e(TAG, "createPreviewCaptureRequest() addTarget(mPreviewSurface);");
                                    builder.addTarget(mPreviewSurface);
                                }
                                previewCaptureRequest = builder.build();
                                mPreviewSession.setRepeatingRequest(previewCaptureRequest, null, mBackgroundHandler);
                            } catch (CameraAccessException e) {
                                Log.e(TAG, "onConfigured: ", e);
                            }

                        }

                        @Override
                        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                            Toast.makeText(getApplicationContext(), "Failed", Toast.LENGTH_SHORT).show();
                        }

                        @Override
                        public void onClosed(@NonNull CameraCaptureSession session) {
                            super.onClosed(session);
                            Log.e(TAG, "startPreview()-->mCameraDevice.createCaptureSession-->onClosed");
                        }
                    }, mBackgroundHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
    private void closePreview(){
        if(mPreviewSession!=null){
            try{
                mPreviewSession.stopRepeating();
                mPreviewSession.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
    private void startRecordPreview(){
        if (null == mCameraDevice || !mTextureView.isAvailable() || null == mPreviewSize) {
            return;
        }
        try {
            //Log.e(TAG, "startPreview()-->下面有closePreviewSession()");
            //closePreviewSession();//这里注释了！！！不然会出现updatePreview(Camera2Activity.java:743)里报错IllegalStateException: Session has been closed; further changes are illegal.
            SurfaceTexture texture = mTextureView.getSurfaceTexture();
            assert texture != null;
            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
            mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

            mPreviewBuilder.addTarget(mPreviewSurface);
            mPreviewBuilder.addTarget(mImageReader.getSurface());

            List<Surface>list = new ArrayList<>();
            list.add(mPreviewSurface);
            list.add(mImageReader.getSurface());

            mCameraDevice.createCaptureSession(list,
                    new CameraCaptureSession.StateCallback() {

                        @Override
                        public void onConfigured(@NonNull CameraCaptureSession session) {
                            mPreviewSession = session;
                            Log.e(TAG, "startPreview()-->mCameraDevice.createCaptureSession-->onConfigured");
                            try {
                                CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

                                if(mPreviewSurface!=null){
                                    // 用于承载预览显示流
                                    Log.e(TAG, "createPreviewCaptureRequest() addTarget(mPreviewSurface);");
                                    builder.addTarget(mPreviewSurface);
                                }
                                builder.addTarget(mImageReader.getSurface());
                                isRecording = true;
                                previewCaptureRequest = builder.build();
                                mPreviewSession.setRepeatingRequest(previewCaptureRequest, null, mBackgroundHandler);
                            } catch (CameraAccessException e) {
                                Log.e(TAG, "onConfigured: ", e);
                                isRecording = false;
                            }

                        }

                        @Override
                        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                            isRecording = false;
                            Toast.makeText(getApplicationContext(), "Failed", Toast.LENGTH_SHORT).show();
                        }

                        @Override
                        public void onClosed(@NonNull CameraCaptureSession session) {
                            super.onClosed(session);
                            Log.e(TAG, "startPreview()-->mCameraDevice.createCaptureSession-->onClosed");
                        }
                    }, mBackgroundHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);
        Log.e(TAG, "onCreate");
        if (getIntent() != null) {
            String cameraIdStr = getIntent().getStringExtra("dmsCameraID");
            if (TextUtils.isEmpty(cameraIdStr)) {
                cameraIdStr = "2";
            }
            Log.e(TAG, "getIntent() != null cameraIdStr=" + cameraIdStr);
            cameraId = Integer.parseInt(cameraIdStr);

            String widthStr = getIntent().getStringExtra("width");
            if (TextUtils.isEmpty(widthStr)) {
                widthStr = "1920";
            }
            mPreviewWidth = Integer.parseInt(widthStr);

            String heightStr = getIntent().getStringExtra("height");
            if (TextUtils.isEmpty(heightStr)) {
                heightStr = "1080";
            }
            mPreviewHeight = Integer.parseInt(heightStr);
        } else {
            cameraId = 2;
            mPreviewWidth = 1920;
            mPreviewHeight = 1080;
        }
        Log.e(TAG, "onCreate mPreviewWidth="+mPreviewWidth+" mPreviewHeight="+mPreviewHeight+" cameraId="+cameraId);

        mPreviewSize = new Size(mPreviewWidth, mPreviewHeight);//预览视频大小
        mVideoSize = new Size(mPreviewWidth, mPreviewHeight);//录制的视频大小
        initView();
        setListener();
    }
    boolean isNeedSaveJpeg = false;
    private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
            = new ImageReader.OnImageAvailableListener() {

        @Override
        public void onImageAvailable(ImageReader reader) {
            final Image image = reader.acquireLatestImage();   // 获取最近一帧图像
            mBackgroundHandler.post(new Runnable() {    // 在子线程执行，防止预览界面卡顿
                @Override
                public void run() {
                    if(image!=null){
                        Log.e(TAG, "width="+image.getWidth()+" image.height="+image.getHeight());

                        byte[] data = YUVUtils.YUV_420_888toNV21(image);
                        if (isNeedSaveJpeg) {
                            isNeedSaveJpeg = false;
                            OutputStream output = null;
                            //存储
                            File file = new File(getExternalCacheDir(), "picture_2592_1800.yuv");
                            try {
                                output = new FileOutputStream(file);
                                output.write(data);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }finally {
                                try {
                                    if(output!=null){
                                        output.close();
                                    }
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                            Log.e(TAG, "save yuv :" + file.getAbsolutePath());
                        }
                        RecordManager.getInstance().addVideoFrameData(data);

                        image.close();//这里需要关闭，不然会卡死预览界面
                    }
                }
            });
        }
    };

    private void initView() {
        mTextureView = (AutoFitTextureView) findViewById(R.id.surfaceView);

        mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
                mPreviewSurface = new Surface(surface);
                openCamera(mPreviewWidth, mPreviewHeight);
            }

            @Override
            public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {

            }

            @Override
            public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {

            }
        });
        findViewById(R.id.bt_back).setOnClickListener(v->{
            finish();
        });
        findViewById(R.id.btnRecord).setOnClickListener(this);
    }
    private void configureTransform(int viewWidth, int viewHeight) {
        if (null == mTextureView || null == mPreviewSize) {
            return;
        }
        int rotation = getWindowManager().getDefaultDisplay().getRotation();
        Matrix matrix = new Matrix();
        RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
        RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
        float centerX = viewRect.centerX();
        float centerY = viewRect.centerY();
        if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
            bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
            matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
            float scale = Math.max(
                    (float) viewHeight / mPreviewSize.getHeight(),
                    (float) viewWidth / mPreviewSize.getWidth());
            matrix.postScale(scale, scale, centerX, centerY);
            matrix.postRotate(90 * (rotation - 2), centerX, centerY);
        }
        mTextureView.setTransform(matrix);
    }

    @SuppressWarnings("MissingPermission")
    private void openCamera(int width, int height) {
        Log.e(TAG, "openCamera: " + width + "---" + height+" cameraId="+cameraId);
        startBackgroundThread();
         configureTransform(width, height);
        Log.e(TAG, "mPreviewWidth="+mPreviewWidth+" mPreviewHeight="+mPreviewHeight);
        mImageReader = ImageReader.newInstance(mPreviewWidth, mPreviewHeight, ImageFormat.YUV_420_888,2);
        mImageReader.setOnImageAvailableListener( mOnImageAvailableListener, mBackgroundHandler);
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            Log.d(TAG, "tryAcquire");
            CameraCharacteristics characteristics = manager.getCameraCharacteristics(String.valueOf(cameraId));
            StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            Size[] sizes = map.getOutputSizes(ImageFormat.JPEG);
            for (int i = 0; i < sizes.length; i++) { //遍历所有Size
                Size itemSize = sizes[i];
                Log.e(TAG, "itemSize 宽=" + itemSize.getWidth() + "高=" + itemSize.getHeight());
            }
            if (map == null) {
                throw new RuntimeException("Cannot get available preview/video sizes");
            }

            Log.e(TAG, "  mPreviewSize.getWidth()=" + mPreviewSize.getWidth() + "  getHeight()=" + mPreviewSize.getHeight());

            int orientation = getResources().getConfiguration().orientation;
            if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                mTextureView.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());
            } else {
                mTextureView.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
            }
            Log.e(TAG, "configureTransform->传递参数："+mPreviewSize.getWidth()+" "+ mPreviewSize.getHeight());
            manager.openCamera(String.valueOf(cameraId), mStateCallback, mBackgroundHandler);
        } catch (CameraAccessException e) {
            Log.e(TAG, "Cannot access the camera");
            Toast.makeText(this, "Cannot access the camera.", Toast.LENGTH_SHORT).show();
            finish();
        } catch (NullPointerException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private void setListener() {

    }

    @Override
    public void onClick(View v) {
        if(v.getId()==R.id.btnRecord){
            if(isRecording){
                Log.e(TAG, "onClick stopRecord");
                isRecording = false;
                //4、停止录制
                RecordManager.getInstance().stopRecord();
            }else{
                Log.e(TAG, "onClick startRecord");
                String path = new File(getExternalCacheDir(), new Date().getTime()+".mp4").getAbsolutePath();
                Log.e(TAG, "path="+path);
                //1、设置路径和尺寸
                RecordManager.getInstance().initVideoFile(mPreviewWidth, mPreviewHeight, path);
                //2、开始录制
                RecordManager.getInstance().startRecord();
                isRecording = true;
            }
        }
    }



    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(mPreviewSession!=null) {
            mPreviewSession.close();
        }
        if(mRecordSession!=null){
            mRecordSession.close();
        }
        if(mCameraDevice!=null) {
            mCameraDevice.close();
        }
        stopBackgroundThread();
    }
}